package com.turing.draw.controller;

import com.alibaba.fastjson.JSONArray;
import com.turing.common.response.ResponseCode;
import com.turing.common.response.ResponseMessage;
import com.turing.common.response.ResponseVO;
import com.turing.common.util.*;
import com.turing.draw.client.reviewer.ReviewerOpClient;
import com.turing.draw.model.domain.FileImage;
import com.turing.draw.model.domain.FileTask;
import com.turing.draw.model.vo.in.FileImagesVo;
import com.turing.draw.model.vo.in.ImageVo;
import com.turing.draw.task.PostProcessorTask;
import com.turing.draw.vo.in.AnalysisResultVo;
import com.turing.draw.vo.in.TextSearchVO;
import com.turing.draw.vo.out.DrawTaskVO;
import com.turing.draw.vo.out.FileTaskVo;
import com.turing.draw.vo.out.ForecastVO;
import com.turing.draw.vo.out.ParaInfoVO;
import com.turing.draw.client.procedure.ProcedureInnerService;
import com.turing.draw.enums.FileStatusEnum;
import com.turing.draw.enums.FileTypeEnum;
import com.turing.draw.enums.TaskStatusEnum;
import com.turing.draw.service.FileImageService;
import com.turing.draw.service.FileTaskService;
import com.turing.draw.service.OcrPageTaskService;
import com.turing.draw.service.DrawTaskService;
import com.turing.orm.handler.DateTableNameParser;
import io.swagger.annotations.Api;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/fileTask")
@Api(tags = {"服务入口"})
public class FileTaskController {

    private Log logger = LogFactory.getLog(FileTaskController.class);

    /**
     * 开基确认书场景ID
     */
    private static final String MERGE_TEMPLATE_ID = "NB2_OCR_BIAOZHUN";

    /**
     * TA申赎场景ID
     */
    private static final String MERGE_NON_STANDARD_TEMPLATE_ID = "NB2_OCR_FEIBIAO";

    @Autowired
    private DrawTaskService drawTaskService;

    @Autowired
    private FileTaskService fileTaskService;

    @Autowired
    private OcrPageTaskService ocrPageTaskService;

    @Autowired
    private FileImageService fileImageService;

    @Autowired
    private ReviewerOpClient reviewerOpClient;

    @Autowired
    private PostProcessorTask postProcessorTask;

    @Autowired
    private ProcedureInnerService procedureInnerService;

    @PostMapping("getAndLockPreProcessTask")
    @Transactional( propagation = Propagation.REQUIRED )
    public ResponseVO<FileTaskVo> getAndLockPreProcessTask( @RequestParam("domain") String domain ){

        FileTaskVo fileTaskVo = new FileTaskVo();
        FileTask fileTask = fileTaskService.getFileTaskByStatus(FileStatusEnum.UPLOADED.getCode());

        Optional.ofNullable(fileTask).ifPresent(t -> BeanUtils.copyProperties( t, fileTaskVo ));

        if(fileTaskVo.equals(new FileTaskVo())){
            return ResponseUtil.create(ResponseCode.TASK_NULL,ResponseMessage.TASK_NULL,0,fileTaskVo);
        }

        String taskSn = fileTaskVo.getTaskSn();
        logger.info("Get pre process task successfully, taskSn is:" + fileTaskVo.getTaskSn());
        boolean update = fileTaskService.lockPreProcessorTask(domain, taskSn);
        logger.info("Lock pre process task, taskSn is:"+taskSn+",result is:"+update);
        if(update){
            return ResponseUtil.success(fileTaskVo);
        }
        return ResponseUtil.success();

    }

    @PostMapping("getAndLockForecastTask")
    @Transactional( propagation = Propagation.REQUIRED )
    public ResponseVO<FileTaskVo> getAndLockForecastTask( @RequestParam("domain") String domain, @RequestParam("templates") String templates){

        FileTaskVo fileTaskVo = new FileTaskVo();
        FileTask fileTask = fileTaskService.getForecastTask(templates);

        Optional.ofNullable(fileTask).ifPresent(t -> BeanUtils.copyProperties( t, fileTaskVo ));

        if(fileTaskVo.equals(new FileTaskVo())){
            return ResponseUtil.create(ResponseCode.TASK_NULL,ResponseMessage.TASK_NOT_FINISH,0,fileTaskVo);
        }

        String taskSn = fileTaskVo.getTaskSn();
        boolean update = fileTaskService.lockForecastTask(domain, taskSn);

        if(update){
            logger.info("lock forecast task successfully, taskSn is:" + fileTaskVo.getTaskSn());
            return ResponseUtil.success(fileTaskVo);
        }
        return ResponseUtil.success();

    }

    @PostMapping("getAndLockAnalysisTask")
    @Transactional( propagation = Propagation.REQUIRED )
    public ResponseVO<FileTaskVo> getAndLockAnalysisTask( @RequestParam("domain") String domain ){

        FileTaskVo fileTaskVo = new FileTaskVo();
        FileTask fileTask = fileTaskService.getAnalysisTask();
        Optional.ofNullable(fileTask).ifPresent(t -> BeanUtils.copyProperties( t, fileTaskVo ));
        if(fileTaskVo.equals(new FileTaskVo())){
            return ResponseUtil.create(ResponseCode.TASK_NULL,ResponseMessage.TASK_NULL,0,fileTaskVo);
        }

        String taskSn = fileTaskVo.getTaskSn();
        logger.info("Get analysis task successfully, taskSn is:" + fileTaskVo.getTaskSn());
        boolean update = fileTaskService.lockAnalysisTask(domain, taskSn);
        logger.info("Lock analysis task, taskSn is:"+taskSn+",result is:"+update);
        return ResponseUtil.success(fileTaskVo);

    }

    @PostMapping("unLockForecastTask")
    public ResponseVO<Object> unLockForecastTask( @RequestParam("taskSn") String taskSn ){
        return ResponseUtil.success(fileTaskService.unLockForecastTask(taskSn));
    }

    @PostMapping("getAndLockCurriculumVitaeTask")
    @Transactional( propagation = Propagation.REQUIRED )
    public ResponseVO<FileTaskVo> getAndLockCurriculumVitaeTask( @RequestParam("domain") String domain ){

        FileTaskVo fileTaskVo = new FileTaskVo();
        FileTask fileTask = fileTaskService.getCurriculumVitaeTask();
        Optional.ofNullable(fileTask).ifPresent(t -> BeanUtils.copyProperties( t, fileTaskVo ));
        if(fileTaskVo.equals(new FileTaskVo())){
            return ResponseUtil.create(ResponseCode.TASK_NULL,ResponseMessage.TASK_NOT_FINISH,0,fileTaskVo);
        }

        String taskSn = fileTaskVo.getTaskSn();
        logger.info("Get analysis task successfully, taskSn is:" + fileTaskVo.getTaskSn());
        boolean update = fileTaskService.lockAnalysisTask(domain, taskSn);
        logger.info("Lock analysis task, taskSn is:"+taskSn+",result is:"+update);
        return ResponseUtil.success(fileTaskVo);

    }

    @PostMapping("fileImageCallBack")
    @Transactional(propagation = Propagation.REQUIRED)
    public ResponseVO<Object> fileImageCallBack(@RequestBody FileImagesVo fileImagesVo) {

        List<String> fileTypes = Arrays.asList(FileTypeEnum.PDF.getName(), FileTypeEnum.PNG.getName(), FileTypeEnum.JPG.getName());
        List<String> docTypes = Arrays.asList(FileTypeEnum.DOC.getName(), FileTypeEnum.DOCX.getName(), FileTypeEnum.TXT.getName());

        String taskSn = fileImagesVo.getTaskSn();

        Integer taskStatus = fileTaskService.getTaskStatusById(taskSn);
        if(taskStatus.equals(FileStatusEnum.NOTEXIST.getCode()) ){
            logger.info("file image callback failed, taskSn :"+taskSn + " not exist!");
            return new ResponseVO<>(ResponseCode.TASK_NULL,ResponseMessage.TASK_NULL);
        }

        // avoid update duplicated
        if(taskStatus.equals(FileStatusEnum.PRETASKPROCESSING.getCode())){
            boolean update = fileTaskService.updateTaskByStatusAndId(taskSn,FileStatusEnum.PRETASKFINISHED.getCode());
            logger.info("file image call back,updating file task status,taskSn is:"+ taskSn + ",result is:"+update);

            // save images
            if (Tools.isNotEmpty(fileImagesVo.getImageVos())) {
                List<ImageVo> imageVos = fileImagesVo.getImageVos();
                FileTask fileTask = fileTaskService.getById(taskSn);
                String fileType = fileTask.getFileType();
                for (ImageVo imageVo : imageVos) {
                    boolean save = false;
                    Integer pageNo = imageVo.getPageNo();
                    if(docTypes.contains(fileType)){
                        save = fileImageService.saveFileImage(fileTask.getParentTaskSn(),taskSn,pageNo,imageVo.getImageUrl(),imageVo.getWidth(),imageVo.getHeight());
                    }else{
                        save = fileImageService.updateFileImage(taskSn,pageNo,imageVo.getImageUrl(),imageVo.getWidth(),imageVo.getHeight());
                    }

                    //logger.info("saving file image,taskSn is:"+taskSn+",pageNo is:"+pageNo+",result is:"+save);

                    if( fileTask.getBelongsElec() == 0 ){
                        boolean queued = ocrPageTaskService.saveFileImage(taskSn,imageVo.getPageNo(),imageVo.getImageUrl());
                        logger.info("put ocr task into queue,taskSn is:"+taskSn+ ",page is:"+ pageNo +",update result is:"+queued);
                    }

                }
                fileTaskService.saveTotalPage(taskSn,imageVos.size());
                String parentTaskSn = fileTask.getParentTaskSn();

                // calculate if all files pre processed
                List<FileTask> fileTasks = fileTaskService.getTasksByParentTaskSn(parentTaskSn);

                boolean taskFinished = fileTasks.stream().allMatch(task -> task.getFileStatus().equals(FileStatusEnum.PRETASKFINISHED.getCode()));

                if (taskFinished) {
                    boolean finishTask = drawTaskService.updateTaskByStatus(parentTaskSn,TaskStatusEnum.PRETASKFINISHED.getCode());
                    procedureInnerService.preTaskFinished(parentTaskSn);
                    logger.info("all pre processor tasks finished,updating task record status,result is:"+finishTask);
                }

            }
        }else{
            logger.warn("don't need to update task status,taskSn is:"+taskSn);
        }

        return ResponseUtil.success();

    }

    @PostMapping("savePdfUrl")
    ResponseVO<Object> savePdfUrl(@RequestParam("pdfUrl")String pdfUrl, @RequestParam("taskSn")String taskSn){

        return ResponseUtil.success(fileTaskService.savePdfUrl(pdfUrl,taskSn));

    }

    @PostMapping("analysisCallback")
    //@Transactional(propagation = Propagation.REQUIRED)
    public ResponseVO<Object> analysisCallback( @RequestBody AnalysisResultVo analysisResultVo ){

        // save first,merge sync
        // analysis result, update fileTask record only
        String taskSn = analysisResultVo.getTaskSn();
        String ocrJsonUrl = analysisResultVo.getOcrJsonUrl();
        Integer taskStatus = fileTaskService.getTaskStatusById(taskSn);
        if(taskStatus.equals(FileStatusEnum.NOTEXIST.getCode()) ){
            logger.info("analysis callback failed, taskSn :"+taskSn + " not exist!");
            return new ResponseVO<>(ResponseCode.TASK_NULL,ResponseMessage.TASK_NULL);
        }

        // avoid update duplicate 处理非成功和失败的逻辑
        if(!taskStatus.equals(FileStatusEnum.OCRTASKFINISHED.getCode()) && !taskStatus.equals(FileStatusEnum.FAILED.getCode())){
            logger.info("start analysis process..");
            boolean analysisFinished = fileTaskService.analysisFinished( taskSn, ocrJsonUrl );
            FileTask fileTask = fileTaskService.getTaskByTaskSn(taskSn);

            // 判断为合并场景，检查新增子任务
            String templateId = fileTask.getTemplateId();
            if (MERGE_TEMPLATE_ID.equals(templateId) || MERGE_NON_STANDARD_TEMPLATE_ID.equals(templateId)) {
                logger.info("**********开始处理合并场景**********");
                ocrPageTaskService.checkAndDivisionTemplate(fileTask.getTaskSn(), fileTask.getTemplateId());
                logger.info("**********合并场景处理结束**********");
            }

            logger.info("analysis call back,update file task,result is:"+analysisFinished);

        }

        return ResponseUtil.success();

    }

    @PostMapping("unLockAnalysisTask")
    public ResponseVO<Object> unLockAnalysisTask( @RequestParam("taskSn") String taskSn ){
        logger.info("unlock analysisi task,taskSn is:"+taskSn);
        return ResponseUtil.success(fileTaskService.unLockAnalysisTask(taskSn));
    }

    // callback save element result,
    // update status async
    @PostMapping("forecastResultCallback")
    @Transactional
    public ResponseVO<Object> forecastResultCallback(@RequestBody Map<String, List<ForecastVO>> forecastEntity , @RequestParam("taskSn") String taskSn,
                                                     @RequestParam("parentTaskSn") String parentTaskSn , @RequestParam("templateId") String templateSn ){
        logger.info(">>>>>>>进入回调处理forecatResultCallback");
        Integer taskStatus = fileTaskService.getTaskStatusById(taskSn);
        logger.info(">>>>>>>进入回调处理，当前任务状态为："+taskStatus);
        if(taskStatus.equals(FileStatusEnum.NOTEXIST.getCode()) ){
            return new ResponseVO<>(ResponseCode.TASK_NULL,ResponseMessage.TASK_NULL);
        }

        // avoid update duplicate
        if(taskStatus.equals(FileStatusEnum.FORECASTTASKPROCESSING.getCode())){
            logger.info(">>>>>开始调用要素值写入 model_content");
            ResponseVO<Object> responseVO = reviewerOpClient.saveForecastResult(forecastEntity, taskSn, templateSn, parentTaskSn);
            if(responseVO.getCode().equals("200")){
                logger.info("saving forecast to reviewer module successfully!");
            }

            boolean update = fileTaskService.updateTaskByStatusAndId(taskSn,FileStatusEnum.FORECASTTASKFINISHED.getCode());


            FileTask sonTask = fileTaskService.getTaskByTaskSn(taskSn);
            List<FileTask> sonFileTasks = fileTaskService.getSonTaskByParentFileTaskSn(sonTask.getParentFileTaskSn());
            logger.info("***************** sonFileTasks size：" + sonFileTasks.size() + "*******************");

            // 不为混合场景下的父子任务，直接走后置脚本更新任务状态
            if(sonFileTasks.size() == 0 && fileTaskService.forecastFinished(parentTaskSn)){
                logger.info("updating forecast file task,result is:"+update);
                // 执行后处理脚本
                postProcessorTask.doPostProcessor(parentTaskSn,taskSn,templateSn);
                ResponseVO<Boolean> booleanResponseVO = procedureInnerService.forecastFinished(parentTaskSn);
                logger.info("update task record status:"+booleanResponseVO.getData());
            } else {
                // 对子任务情况进行处理
                boolean isAllSonFileTaskFinish = true;
                if (sonFileTasks.size() > 0) {
                    FileTask fatherTask = fileTaskService.getTaskByTaskSn(sonTask.getParentFileTaskSn());
                    String fatherTaskSn = fatherTask.getTaskSn();
                    String fatherParentTaskSn = fatherTask.getParentTaskSn();

                    // 执行后处理脚本
                    logger.info("**************执行后处理脚本 taskSn: " + taskSn + "*****************");
                    postProcessorTask.doPostProcessor(parentTaskSn,taskSn,templateSn);

                    // 循环判断子任务状态
                    for (FileTask sonFileTask : sonFileTasks) {
                        if (FileStatusEnum.FAILED.getCode().equals(sonFileTask.getFileStatus())) {
                            // 子任务存在失败状态则更新父任务为失败 todo 更新剩余子任务为失败 失败重试？
                            fileTaskService.updateTaskByStatusAndId(fatherTaskSn, FileStatusEnum.FAILED.getCode());
                            logger.info("子任务存在失败状态则更新父任务为失败>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
                            isAllSonFileTaskFinish = false;
                            logger.error("子任务失败,taskSn为:" + sonFileTask.getTaskSn());
                        } else if (!FileStatusEnum.FORECASTTASKFINISHED.getCode().equals(sonFileTask.getFileStatus())) {
                            // 所有子任务状态不为预测完成 则不更新父任务状态
                            logger.info("所有子任务状态不为预测完成 则不更新父任务状态>>>>>>>>>>>>>>>>>>>>>>>>");
                            isAllSonFileTaskFinish = false;
                        }
                    }
                    logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<所有子任务是否都完成 isAllSonFileTaskFinish： " + isAllSonFileTaskFinish);
                    if (isAllSonFileTaskFinish) {
                        // 所有子任务都完成
                        fileTaskService.updateTaskByStatusAndId(fatherTaskSn, FileStatusEnum.MERGETEMPLATEFINISHED.getCode());
                        ResponseVO<Boolean> booleanResponseVO = procedureInnerService.forecastFinished(fatherParentTaskSn);
                        logger.info("****************更新父任务状态完成 status :" + booleanResponseVO.getData() + "***********************");
                    }
                }



            }
        }

        return ResponseUtil.success();

    }

    @PostMapping("preprocessorFailedCallback")
    @Transactional( propagation = Propagation.REQUIRED )
    public ResponseVO<Object> preprocessorFailedCallback( @RequestParam("taskSn") String taskSn ){

        String parentTaskSn = fileTaskService.getParentTaskSn(taskSn);
        fileTaskService.tasksFailed(parentTaskSn,"10001:预处理失败");
        return ResponseUtil.success();

    }

    @PostMapping("queryParentTask")
    public String queryParentTask(@RequestParam("taskSn") String taskSn){
        return fileTaskService.queryParentTask(taskSn);
    }

    @GetMapping("getPageJson")
    public ResponseVO<Object> getPageJson(@RequestParam("taskSn") String taskSn,@RequestParam("page") Integer page){
        return ResponseUtil.success(fileImageService.getPageJson(taskSn,page));
    }

    @PostMapping("batchGetPageJson")
    public ResponseVO<Object> batchGetPageJson(@RequestParam("taskSn") String taskSn,@RequestBody List<Integer> pages){
        return ResponseUtil.success(fileImageService.batchGetPageJson(taskSn,pages));
    }

    @PostMapping("queryParentTaskVersion")
    public Integer queryParentTaskVersion(@RequestParam("taskSn") String taskSn){
        return fileTaskService.getTaskVersion(taskSn);
    }

    @GetMapping("getTaskNameRes")
    public ResponseVO<String> getTaskNameRes(@RequestParam("taskSn") String taskSn){
        FileTask fileTask = fileTaskService.getById(taskSn);
        if (fileTask != null) {
            return new ResponseVO<>(fileTask.getFileName(), ResponseCode.SUCCESS, ResponseMessage.SUCCESS, 1);
        }
        return new ResponseVO<>("", ResponseCode.SUCCESS, ResponseMessage.SUCCESS, 1);
    }

    @GetMapping("getChildTasks")
    public List<DrawTaskVO> getChildTasks(@RequestParam("taskSn") String taskSn){
        List<DrawTaskVO> childTaskVOs = new ArrayList<>();
        List<FileTask> childTasks = fileTaskService.getChildTasks(taskSn);
        if (!CollectionUtils.isEmpty(childTasks)) {
            childTasks.forEach((childTask) -> {
                DrawTaskVO drawTask = new DrawTaskVO();
                BeanUtils.copyProperties(childTask,drawTask);
                Optional.ofNullable(childTask.getFileName()).filter(n->!StringUtils.isEmpty(n)).ifPresent(drawTask::setTaskName);
                Optional.ofNullable(childTask.getCleanTextUrl()).filter(n->!StringUtils.isEmpty(n)).ifPresent(drawTask::setCleanTextPath);
                Optional.ofNullable(childTask.getJSONUrl()).filter(n->!StringUtils.isEmpty(n)).ifPresent(drawTask::setJsonUrl);
                Optional.ofNullable(childTask.getTableJsonUrl()).filter(n->!StringUtils.isEmpty(n)).ifPresent(drawTask::setTableJsonUrl);
                childTaskVOs.add(drawTask);
            });
        }
        return childTaskVOs;
    }

    @PostMapping("locateText")
    public ResponseVO<Object> locateText(@RequestBody TextSearchVO textSearchVO){
        FileTask task = fileTaskService.getById(textSearchVO.getChildTaskSn());
        List<ParaInfoVO> paraInfoVOS = fileTaskService.initParaInfo(textSearchVO, task);
        return new ResponseVO<>(paraInfoVOS, ResponseCode.SUCCESS, ResponseMessage.SUCCESS,0);
    }

    @PostMapping("testDelete")
    public void testDelete(){
        FdfsUtils.delete_file("http://192.168.1.73:7003/group1/M00/00/2F/wKgBSWGeCs2Aef_ZAAIzETt9npA220.jpg");
    }

    @PostMapping("finishReview")
    public ResponseVO<Object> finishReview( @RequestParam("taskSn") String taskSn ){

        return ResponseUtil.success(fileTaskService.finishReview(taskSn));

    }

    @PostMapping("transferData")
    public ResponseVO<Object> transferData(){

        String s = FileUtils.loadJson("http://192.168.1.73:7003/group1/M00/00/C4/wKgBSWId2EeAXFGfAARw-aLzKG071.json");
        JSONArray jsonArray = JSONArray.parseArray(s.trim());
        for (int i = 0; i < jsonArray.size(); i++) {
            Object o = jsonArray.get(i);
            String s1 = FdfsUtils.uploadBytes(o.toString());
            DateTableNameParser.setTaskSn(948253858096873472L);
            boolean update = fileImageService.lambdaUpdate().eq(FileImage::getTaskSn, "948253858096873472")
                    .eq(FileImage::getPageNo, i)
                    .set(FileImage::getJsonUrl, s1).update();
        }

        return new ResponseVO<>(null, ResponseCode.SUCCESS, ResponseMessage.SUCCESS,0);

    }

    @GetMapping("getPagesSize")
    public ResponseVO<Object> getPagesSize( @RequestParam("taskSn") String taskSn ){

        return ResponseUtil.success(fileImageService.getPagesSize(taskSn));

    }

    @GetMapping("getTemplateIdsByDrawTaskSn")
    public ResponseVO<List<FileTaskVo>> getTemplateIdListByDrawTaskSn(@RequestParam("drawTaskSn") String drawTaskSn ){
        return ResponseUtil.success(fileTaskService.getTemplateIdListByDrawTaskSn(drawTaskSn));
    }

}
